<?php
declare (strict_types=1);

namespace app\api\common\middleware;

use app\api\controller\admin\setting\LoginController;
use app\Request;
use think\Request as ThinkRequest;
use think\facade\Config;
use Emarref\Jwt\Claim;
use Emarref\Jwt\Encoding\Base64;
use Emarref\Jwt\Token;
use Emarref\Jwt\Jwt;
use Emarref\Jwt\Algorithm;
use Emarref\Jwt\Encryption\Factory;
use Emarref\Jwt\Verification\Context;
use think\facade\Request as FacadeRequest;

//重复命名：
use Emarref\Jwt\Exception as JWT_Exception;
use \Emarref\Jwt as JWT_ROOT;

use app\service\AdminAdmin as S;

//前置中间件：管理员鉴定
class AdminAuthTokenMiddleware
{
    /**
     * 处理请求
     */
    public function handle(Request $request, \Closure $next)
    {
        $serializedToken = $request->header('Authorization', null);
        if ($serializedToken === null) {
            return ret_value(-1, "没有登录", null, 401);
        }

        $lms_config = Config::get('lms_common');

        $jwt = new Jwt();

        $algorithm  = new \Emarref\Jwt\Algorithm\Hs256($lms_config['JWT_Password']);
        $encryption = Factory::create($algorithm);
        $context    = new Context($encryption);
        $context->setIssuer($lms_config['JWT_Issuser']);
        $context->setSubject($lms_config['JWT_Subject']);

        try {
            $token = $jwt->deserialize($serializedToken);
            //Token 过期刷新处理
            $payload              = ($token->getPayload());    //获取Payload
            $exp                  = ($payload->findClaimByName("exp")->getValue());    //过期时间
            $refresh_token_expire = ($payload->findClaimByName("refresh_token_expire")->getValue());    //刷新时间
            $now                  = time();
            //重新生成Token
            if ($exp < $now) {
                //刷新时间也过期，则需要重新登录
                if ($refresh_token_expire < $now) {
                    return ret_value(-1, "登录超时", null, 401);
                } else {
                    //重新生成Token
                    return ret_value(0, "refreshToken",
                        $this->createToken(
                            $payload->findClaimByName("jti")->getValue(),    //jti也用原来的，最好用新的
                            $payload->findClaimByName("data")->getValue()    //数据还是原来的
                        ),
                        401);
                }
            }

            $jwt->verify($token, $context);
            //注入数据
            $request->AdminInfo = $token->getPayload()->findClaimByName("data")->getValue();
            return $next($request);
        } catch (JWT_ROOT\Exception\InvalidAudienceException $e) {
            return ret_value(-2, "AudienceException", null, 401);
        } catch (\Emarref\Jwt\Exception\VerificationException $e) {
            return ret_value(-1, "Token验证失败", null, 401);
        } catch (\RuntimeException $e) {
            return ret_value(-3, $e->getMessage(), null, 500);
        } catch (\Exception $e) {
            return ret_value(-4, $e->getMessage(), null, 500);
        } finally {

        }
        halt("???");
    }

    /**
     * createToken 生成Token，jwtID为客户端的 验证码ID（也可以服务端提供）
     *
     * @param $jwtID
     * @param $dataClaim
     *
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function createToken($jwtID, $dataClaim): array
    {
        $lms_config = Config::get('lms_common');
        $token      = new Token();
        $jwt        = new Jwt();

        //给Payload设置数据（Claim）
        $token->addClaim(new Claim\Expiration(new \DateTime('+1 days')));
        $token->addClaim(new Claim\IssuedAt(new \DateTime('now')));
        $token->addClaim(new Claim\Issuer($lms_config['JWT_Issuser']));
        $token->addClaim(new Claim\JwtId(uniqid((string)$jwtID, true)));    //session_create_id()
        $token->addClaim(new Claim\NotBefore(new \DateTime('now')));
        $token->addClaim(new Claim\Subject($lms_config['JWT_Subject']));

        // Custom claims are supported
        $token->addClaim(new Claim\PublicClaim('refresh_token_expire', (new \DateTime('+3 days'))->getTimestamp()));
        $token->addClaim(new Claim\PrivateClaim('data', $dataClaim));

        //加密
        $algorithm  = new Algorithm\Hs256($lms_config['JWT_Password']);
        $encryption = Factory::create($algorithm);

        $user  = \think\facade\Db::connect('mysql')
            ->name('admin_user')
            ->field('user.*, role.role_name, role.rules, role.status AS role_status')
            ->alias("user")
            ->leftJoin('admin_role role', 'user.roles = role.id')
            ->where('user.id', $dataClaim['id'])
            ->where('user.delete_time', null)
            ->where('role.delete_time', null)
            ->where('role.status', 1)
            ->find();
        $rules = \think\facade\Db::connect('mysql')
            ->name('admin_permission')
            ->when($user["level"] > 0, function ($query) use ($user) {
                $query->whereIn('id', $user['rules']);
            })
            ->where('delete_time', null)
            ->where('status', 1)
            ->order('sort', 'asc')
            ->select();
        $info  = [
            "account"   => $user["account"],
            "id"        => $user["id"],
            "real_name" => $user["real_name"],
            "roles"     => $user["roles"],
            "level"     => $user["level"],
            "role_name" => $user["role_name"],
        ];
        $token = $jwt->serialize($token, $encryption);
        return compact('token', 'info', 'rules');
    }

}
